<template>
  <div
    class="virtual-grid-container"
    :style="{
      height: containerHeight + 'px',
      width: containerWidth + 'px',
    }"
    @scroll="handleScroll"
    ref="containerRef"
  >
    <div
      class="virtual-grid-content"
      :style="{
        height: totalHeight + 'px',
        width: totalWidth + 'px',
      }"
    >
      <div
        class="virtual-grid-items"
        :style="{
          transform: `translate(${offsetX}px, ${offsetY}px)`,
          display: 'grid',
          gridTemplateColumns: `repeat(${displayCols}, ${itemWidth}px)`,
          gridTemplateRows: `repeat(${displayRows}, ${itemHeight}px)`,
          gap: gap + 'px',
          padding: padding + 'px',
        }"
      >
        <div
          v-for="item in visibleItems"
          :key="item.id"
          class="virtual-grid-item"
          :style="{
            height: itemHeight + 'px',
            width: itemWidth + 'px',
          }"
        >
          <slot
            :item="item"
            :index="getItemIndex(item)"
            :row="getItemRow(item)"
            :col="getItemCol(item)"
          >
            {{ item.text }}
          </slot>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from "vue";

interface VirtualGridItem {
  id: number;
  text: string;
  [key: string]: any;
}

interface Props {
  items: VirtualGridItem[];
  itemHeight: number;
  itemWidth: number;
  containerHeight: number;
  containerWidth: number;
  columns: number;
  gap?: number;
  padding?: number;
  bufferSize?: number;
}

const props = withDefaults(defineProps<Props>(), {
  itemHeight: 200,
  itemWidth: 200,
  containerHeight: 400,
  containerWidth: 800,
  columns: 3,
  gap: 10,
  padding: 16,
  bufferSize: 2,
});

const containerRef = ref<HTMLElement>();
const scrollTop = ref(0);
const scrollLeft = ref(0);
let ticking = false;

// 计算总行数
const rows = computed(() => Math.ceil(props.items.length / props.columns));

// 计算总宽度（包含间距和边距）
const totalWidth = computed(
  () =>
    props.columns * props.itemWidth +
    (props.columns - 1) * props.gap +
    props.padding * 2
);

// 计算总高度（包含间距和边距）
const totalHeight = computed(
  () =>
    rows.value * props.itemHeight +
    (rows.value - 1) * props.gap +
    props.padding * 2
);

// 计算可见区域（行）
const visibleStartRow = computed(() => {
  const start = Math.floor(scrollTop.value / (props.itemHeight + props.gap));
  return Math.max(0, start - props.bufferSize);
});

const visibleEndRow = computed(() => {
  const end =
    visibleStartRow.value +
    Math.ceil(props.containerHeight / (props.itemHeight + props.gap)) +
    props.bufferSize * 2;
  return Math.min(end, rows.value);
});

const displayRows = computed(() => {
  return visibleEndRow.value - visibleStartRow.value;
});

// 计算可见区域（列）
const visibleStartCol = computed(() => {
  const start = Math.floor(scrollLeft.value / (props.itemWidth + props.gap));
  return Math.max(0, start - props.bufferSize);
});

const visibleEndCol = computed(() => {
  const end =
    visibleStartCol.value +
    Math.ceil(props.containerWidth / (props.itemWidth + props.gap)) +
    props.bufferSize * 2;
  return Math.min(end, props.columns);
});

const displayCols = computed(() => {
  return visibleEndCol.value - visibleStartCol.value;
});

// 计算可见项目
const visibleItems = computed(() => {
  const result: VirtualGridItem[] = [];

  for (let row = visibleStartRow.value; row < visibleEndRow.value; row++) {
    for (let col = visibleStartCol.value; col < visibleEndCol.value; col++) {
      const index = row * props.columns + col;
      if (index < props.items.length) {
        result.push(props.items[index]);
      }
    }
  }

  return result;
});

// 计算偏移量
const offsetX = computed(
  () => visibleStartCol.value * (props.itemWidth + props.gap)
);
const offsetY = computed(
  () => visibleStartRow.value * (props.itemHeight + props.gap)
);

// 获取项目在原始数组中的索引
const getItemIndex = (item: VirtualGridItem) => {
  return props.items.findIndex((i) => i.id === item.id);
};

// 获取项目所在的行
const getItemRow = (item: VirtualGridItem) => {
  const index = getItemIndex(item);
  return Math.floor(index / props.columns);
};

// 获取项目所在的列
const getItemCol = (item: VirtualGridItem) => {
  const index = getItemIndex(item);
  return index % props.columns;
};

// 节流滚动处理
const handleScroll = (event: Event) => {
  if (!ticking) {
    requestAnimationFrame(() => {
      const target = event.target as HTMLElement;
      scrollTop.value = target.scrollTop;
      scrollLeft.value = target.scrollLeft;
      ticking = false;
    });
    ticking = true;
  }
};

// 滚动到指定位置
const scrollTo = (index: number) => {
  if (containerRef.value) {
    const row = Math.floor(index / props.columns);
    const col = index % props.columns;
    const scrollTop = row * (props.itemHeight + props.gap);
    const scrollLeft = col * (props.itemWidth + props.gap);
    containerRef.value.scrollTop = scrollTop;
    containerRef.value.scrollLeft = scrollLeft;
  }
};

// 滚动到指定行列
const scrollToPosition = (row: number, col: number) => {
  if (containerRef.value) {
    const scrollTop = row * (props.itemHeight + props.gap);
    const scrollLeft = col * (props.itemWidth + props.gap);
    containerRef.value.scrollTop = scrollTop;
    containerRef.value.scrollLeft = scrollLeft;
  }
};

// 滚动到指定行
const scrollToRow = (rowIndex: number) => {
  if (containerRef.value) {
    const scrollTop = rowIndex * (props.itemHeight + props.gap);
    containerRef.value.scrollTop = scrollTop;
  }
};

// 滚动到指定列
const scrollToCol = (colIndex: number) => {
  if (containerRef.value) {
    const scrollLeft = colIndex * (props.itemWidth + props.gap);
    containerRef.value.scrollLeft = scrollLeft;
  }
};

// 获取当前可见的区域范围
const getVisibleRange = () => {
  return {
    rows: {
      start: visibleStartRow.value,
      end: visibleEndRow.value,
    },
    cols: {
      start: visibleStartCol.value,
      end: visibleEndCol.value,
    },
  };
};

// 获取当前滚动位置
const getScrollPosition = () => {
  return {
    scrollTop: scrollTop.value,
    scrollLeft: scrollLeft.value,
  };
};

// 暴露方法给父组件
defineExpose({
  scrollTo,
  scrollToPosition,
  scrollToRow,
  scrollToCol,
  getVisibleRange,
  getScrollPosition,
});
</script>

<style scoped>
.virtual-grid-container {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: auto;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  position: relative;
}

.virtual-grid-content {
  position: relative;
}

.virtual-grid-items {
  position: absolute;
  top: 0;
  left: 0;
}

.virtual-grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #f0f0f0;
  border-radius: 4px;
  font-size: 14px;
  color: #333;
  background: #fafafa;
  transition: all 0.2s;
  cursor: pointer;
  box-sizing: border-box;
}

.virtual-grid-item:hover {
  background: #f0f0f0;
  border-color: #d0d0d0;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* 自定义滚动条样式 */
.virtual-grid-container::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.virtual-grid-container::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}

.virtual-grid-container::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}

.virtual-grid-container::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .virtual-grid-items {
    grid-template-columns: repeat(2, 1fr) !important;
  }
}

@media (max-width: 480px) {
  .virtual-grid-items {
    grid-template-columns: 1fr !important;
  }
}
</style>
